package com.zdp.contentcenter.auth;

import com.zdp.contentcenter.security.MySecurityException;
import com.zdp.contentcenter.util.JwtOperator;
import io.jsonwebtoken.Claims;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.Objects;

/**
 * @author sesshomaru
 * @date 2020/9/26 20:28
 * 1.先引入aop的依赖
 */
// aop验证用户登录授权
@Component
@Aspect
@Slf4j
public class AuthAspect {

    @Autowired
    private JwtOperator jwtOperator;

    // 验证用户登录token是否正确
    // 环绕通知 只要加了@CheckLogin都会走到这里
    @Around("@annotation(com.zdp.contentcenter.auth.CheckLogin)")
    public Object checkLogin(ProceedingJoinPoint point) throws Throwable {
        try {
            // 1.从header里获取token
            // todo 这里还需要判断是否空指针
            // spring提供的静态方法获取request
            checkToken();

            log.info("aop验证用户登录授权成功...");
           // return point.proceed();

        } catch (Throwable throwable) {
            log.warn("aop验证用户登录授权失败...");
            throw new MySecurityException("token不合法!");
        }
        return point.proceed();
    }

    // 验证用户权限
    @Around("@annotation(com.zdp.contentcenter.auth.CheckAuthorization)")
    public Object checkAuthorization(ProceedingJoinPoint point) throws Throwable {
        try {
            // 1. 验证token是否合法
            checkToken();
            // 2. 验证用户角色是否匹配
            HttpServletRequest request = getHttpServletRequest();
            // 从之前验证token时存入request的Attribute中获取解析出来token密文中的role
            String role = (String) request.getAttribute("role");
            MethodSignature signature = (MethodSignature) point.getSignature();
            // 获取注解上的内容
            // 获取加了@CheckAuthorization方法的定义
            Method method = signature.getMethod();
            CheckAuthorization checkAuthorization = method.getAnnotation(CheckAuthorization.class);
            // 注解中的值
            String value = checkAuthorization.value();
            if (!Objects.equals(role, value)) {
                throw new MySecurityException("用户无权访问");
            }
            //return point.proceed();
        } catch (Throwable throwable) {
            throw new MySecurityException("用户无权访问", throwable);
        }
        // 注意这里为了避免目标方法的异常配置上面的用户无权访问异常捕获 要写到外面将目标方法的异常抛出
        return point.proceed();
    }

    // 检验token是否合法
    private void checkToken() {
        HttpServletRequest request = getHttpServletRequest();

        String token = request.getHeader("x-token");

        // 2.校验token是否合法 & 是否过期，如果不合法或者已过期，就抛异常；如过合法就放行
        Boolean isValid = jwtOperator.validateToken(token);
        if (!isValid) {
            // 不合法抛出异常会被 (GlobalExceptionErrorHandler) 全局异常处理捕获，全局异常处理回返回对应的错误响应
            throw new MySecurityException("token不合法!");
        }

        // 合法 执行目标方法
        // 3.如果校验成功，那么就将用户的信息设置到request的attribute里面
        Claims claims = jwtOperator.getClaimsFromToken(token);
        request.setAttribute("id", claims.get("id"));
        request.setAttribute("wxNickName", claims.get("wxNickName"));
        request.setAttribute("role", claims.get("role"));
    }

    private HttpServletRequest getHttpServletRequest() {
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) requestAttributes;
        return servletRequestAttributes.getRequest();
    }
}
